#ifndef NYU_TREE_H_
#define NYU_TREE_H_

#include "nyu_common.h"
#include "nyu_var_list.h"
#include "nyu_string.h"

typedef enum {
    NYU_TREE_TYPE_ERROR,
    NYU_TREE_TYPE_LIST,
    NYU_TREE_TYPE_EXPR_PUT,
    NYU_TREE_TYPE_EXPR_SET,
    NYU_TREE_TYPE_EXPR_ADD_ASSIGN,
    NYU_TREE_TYPE_EXPR_SUB_ASSIGN,
    NYU_TREE_TYPE_EXPR_MUL_ASSIGN,
    NYU_TREE_TYPE_EXPR_DIV_ASSIGN,
    NYU_TREE_TYPE_EXPR_MOD_ASSIGN,
    NYU_TREE_TYPE_EXPR_LSHIFT_ASSIGN,
    NYU_TREE_TYPE_EXPR_RSHIFT_ASSIGN,
    NYU_TREE_TYPE_EXPR_BAND_ASSIGN,
    NYU_TREE_TYPE_EXPR_BXOR_ASSIGN,
    NYU_TREE_TYPE_EXPR_BOR_ASSIGN,
    NYU_TREE_TYPE_EXPR_LAND_ASSIGN,
    NYU_TREE_TYPE_EXPR_LOR_ASSIGN,
    NYU_TREE_TYPE_EXPR_SWAP,
    NYU_TREE_TYPE_EXPR_COND,
    NYU_TREE_TYPE_EXPR_LOR,
    NYU_TREE_TYPE_EXPR_LXOR,
    NYU_TREE_TYPE_EXPR_LAND,
    NYU_TREE_TYPE_EXPR_BOR,
    NYU_TREE_TYPE_EXPR_BXOR,
    NYU_TREE_TYPE_EXPR_BAND,
    NYU_TREE_TYPE_EXPR_EQ,
    NYU_TREE_TYPE_EXPR_NE,
    NYU_TREE_TYPE_EXPR_EQ3,
    NYU_TREE_TYPE_EXPR_NE3,
    NYU_TREE_TYPE_EXPR_LT,
    NYU_TREE_TYPE_EXPR_LE,
    NYU_TREE_TYPE_EXPR_GT,
    NYU_TREE_TYPE_EXPR_GE,
    NYU_TREE_TYPE_EXPR_CMP,
    NYU_TREE_TYPE_EXPR_OWN_PROTO,
    NYU_TREE_TYPE_EXPR_OWN_MEMBER,
    NYU_TREE_TYPE_EXPR_LSHIFT,
    NYU_TREE_TYPE_EXPR_RSHIFT,
    NYU_TREE_TYPE_EXPR_ADD,
    NYU_TREE_TYPE_EXPR_SUB,
    NYU_TREE_TYPE_EXPR_MUL,
    NYU_TREE_TYPE_EXPR_DIV,
    NYU_TREE_TYPE_EXPR_MOD,
    NYU_TREE_TYPE_EXPR_LNOT,
    NYU_TREE_TYPE_EXPR_BNOT,
    NYU_TREE_TYPE_EXPR_NEGAT,
    NYU_TREE_TYPE_EXPR_INC,
    NYU_TREE_TYPE_EXPR_DEC,
    NYU_TREE_TYPE_EXPR_POST_INC,
    NYU_TREE_TYPE_EXPR_POST_DEC,
    NYU_TREE_TYPE_EXPR_SLOT_ACCESS,
    NYU_TREE_TYPE_SLOT_ACCESS,
    NYU_TREE_TYPE_CALL,
    NYU_TREE_TYPE_BLOCK,
    NYU_TREE_TYPE_OBJECT,
    NYU_TREE_TYPE_OBJECT_SLOT,
    NYU_TREE_TYPE_ARRAY,
    NYU_TREE_TYPE_RANGE,
    NYU_TREE_TYPE_SYMBOL,
    NYU_TREE_TYPE_STRVAL,
    NYU_TREE_TYPE_INTVAL,
    NYU_TREE_TYPE_FLTVAL,
    NYU_TREE_TYPE_ARGREF,
    NYU_TREE_TYPE_ARGLEN,
    NYU_TREE_TYPE_CURBLK,
} NyuTreeType;

#define NYU_TREE(X) ((NyuTree *)(X))

struct NyuTree {
    NyuTreeType type;
};

#define NYU_TREE_LIST(X) ((NyuTreeList *)(X))

typedef struct NyuTreeList NyuTreeList;
struct NyuTreeList {
    NyuTree base;
    NyuTree *value;
    NyuTreeList *next;
};

NyuTreeList *nyu_tree_list_prepend(NyuTreeList *self, NyuTree *value);
NyuTreeList *nyu_tree_list_reverse(NyuTreeList *self);

#define NYU_TREE_EXPR(X) ((NyuTreeExpr *)(X))

typedef struct NyuTreeExpr NyuTreeExpr;
struct NyuTreeExpr {
    NyuTree base;
    NyuTree *lnode;
    NyuTree *rnode;
};

NyuTree *nyu_tree_expr_new(NyuTreeType type,
                           NyuTree *lnode,
                           NyuTree *rnode);


#define NYU_TREE_COND(X) ((NyuTreeCond *)(X))

typedef struct NyuTreeCond NyuTreeCond;
struct NyuTreeCond {
    NyuTree base;
    NyuTree *cond;
    NyuTree *tnode;
    NyuTree *fnode;
};

NyuTree *nyu_tree_cond_new(NyuTree *cond,
                           NyuTree *tnode,
                           NyuTree *fnode);


#define NYU_TREE_CALL(X) ((NyuTreeCall *)(X))

typedef struct NyuTreeCall NyuTreeCall;
struct NyuTreeCall {
    NyuTree base;
    NyuTree *prim;
    NyuTreeList *args;
    long argc;
};

NyuTree *nyu_tree_call_new(NyuTree *prim, NyuTreeList *args);


#define NYU_TREE_SLOT_ACCESS(X) ((NyuTreeSlotAccess *)(X))

typedef struct NyuTreeSlotAccess NyuTreeSlotAccess;
struct NyuTreeSlotAccess {
    NyuTree base;
    NyuTree *prim;
    NyuString *name;
};

NyuTree *nyu_tree_slot_access_new(NyuTree *prim, char *name);


#define NYU_TREE_BLOCK(X) ((NyuTreeBlock *)(X))

typedef struct NyuTreeBlock NyuTreeBlock;
struct NyuTreeBlock {
    NyuTree base;
    NyuTreeList *stmts;
};

NyuTree *nyu_tree_block_new(NyuTreeList *stmts);


#define NYU_TREE_OBJECT(X) ((NyuTreeObject *)(X))

typedef struct NyuTreeObject NyuTreeObject;
struct NyuTreeObject {
    NyuTree base;
    NyuTreeList *slots;
};

NyuTree *nyu_tree_object_new(NyuTreeList *slots);


#define NYU_TREE_OBJECT_SLOT(X) ((NyuTreeObjectSlot *)(X))

typedef struct NyuTreeObjectSlot NyuTreeObjectSlot;
struct NyuTreeObjectSlot {
    NyuTree base;
    NyuString *name;
    NyuTree *expr;
};

NyuTree *nyu_tree_object_slot_new(char *name, NyuTree *expr);


#define NYU_TREE_ARRAY(X) ((NyuTreeArray *)(X))

typedef struct NyuTreeArray NyuTreeArray;
struct NyuTreeArray {
    NyuTree base;
    NyuTreeList *elms;
    long len;
};

NyuTree *nyu_tree_array_new(NyuTreeList *elms);


#define NYU_TREE_RANGE(X) ((NyuTreeRange *)(X))

typedef struct NyuTreeRange NyuTreeRange;
struct NyuTreeRange {
    NyuTree base;
    NyuTree *from;
    NyuTree *to;
    NyuTree *step;
    int is_include_border;
};

NyuTree *nyu_tree_range_new(NyuTree *from,
                            NyuTree *to,
                            NyuTree *step,
                            int is_include_border);


#define NYU_TREE_SYMBOL(X) ((NyuTreeSymbol *)(X))

typedef struct NyuTreeSymbol NyuTreeSymbol;
struct NyuTreeSymbol {
    NyuTree base;
    char *symbol;
    NyuVarData **ref;
};

NyuTree *nyu_tree_symbol_new(char *symbol);


#define NYU_TREE_INTVAL(X) ((NyuTreeIntval *)(X))

typedef struct NyuTreeIntval NyuTreeIntval;
struct NyuTreeIntval {
    NyuTree base;
    long intval;
};

NyuTree *nyu_tree_intval_new(long intval);


#define NYU_TREE_FLTVAL(X) ((NyuTreeFltval *)(X))

typedef struct NyuTreeFltval NyuTreeFltval;
struct NyuTreeFltval {
    NyuTree base;
    double fltval;
};

NyuTree *nyu_tree_fltval_new(double fltval);


#define NYU_TREE_STRVAL(X) ((NyuTreeStrval *)(X))

typedef struct NyuTreeStrval NyuTreeStrval;
struct NyuTreeStrval {
    NyuTree base;
    char *strval;
    NyuString *strobj;
};

NyuTree *nyu_tree_strval_new(char *strval);


#define NYU_TREE_ARGREF(X) ((NyuTreeArgref *)(X))

typedef struct NyuTreeArgref NyuTreeArgref;
struct NyuTreeArgref {
    NyuTree base;
    long idx;
};

NyuTree *nyu_tree_argref_new(long idx);


#define NYU_TREE_ARGLEN(X) ((NyuTreeArglen *)(X))

typedef struct NyuTreeArglen NyuTreeArglen;
struct NyuTreeArglen {
    NyuTree base;
};

NyuTree *nyu_tree_arglen_new();


#define NYU_TREE_CURBLK(X) ((NyuTreeCurblk *)(X))

typedef struct NyuTreeCurblk NyuTreeCurblk;
struct NyuTreeCurblk {
    NyuTree base;
};

NyuTree *nyu_tree_curblk_new();


void nyu_tree_dump(NyuTree *self);

#endif /* NYU_TREE_H_ */
